#pragma once
/*
    基于抽象层实现具象层
    1. 对于网络通信模块的实现[陈硕大佬的Muduo库]
    2. LV格式协议的实现 [BaseProtocol]
    3. 基于Muduo库的Connection具象层实现 [BaseConnection]
    4. MuduoServer 服务端的具象实现 [BaseServer]
    5. MuduoClient 客户端的具象实现 [BaseClient]
*/
#include <muduo/net/TcpServer.h>
#include <muduo/net/TcpClient.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/Buffer.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/base/CountDownLatch.h>
#include "detail.hpp"
#include "fields.hpp"
#include "abstract.hpp"
#include "message.hpp"
#include <mutex>
#include <unordered_map>

namespace tmh_rpc
{

    // ---------------------------------- 基于Muduo库对BaseBuffer进行具象实现 ---------------------------
    class MuduoBuffer : public BaseBuffer
    {
    public:
        using ptr = std::shared_ptr<MuduoBuffer>;
        MuduoBuffer(muduo::net::Buffer *buf) : _buf(buf) {}

        virtual size_t readableSize() override
        // 返回当前缓冲区数据大小
        {
            return _buf->readableBytes();
        }

        virtual int32_t peekInt32() override
        // 尝试从缓冲区中取出4字节数据
        {
            // muduo库是一个网络库,从缓冲区取出一个4字节整型,会进行网络字节序的转换
            return _buf->peekInt32();
        }
        virtual void retrieveInt32() override
        // 删除缓冲区4字节数据
        {
            _buf->retrieveInt32();
        }
        virtual int32_t readInt32() override
        // 读取缓冲区4字节数据，并且删除数据
        {
            return _buf->readInt32();
        }

        virtual string retrieveAsString(size_t len) override
        // 从缓冲区取出指定长度的数据
        {
            return _buf->retrieveAsString(len);
        }

    private:
        muduo::net::Buffer *_buf;
    };

    // 生产MuduoBuffer的工厂类
    class BufferFactory
    {
    public:
        template <typename... Args>
        static BaseBuffer::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoBuffer>(std::forward<Args>(args)...);
        }
    };

    // --------------------------------- LV格式的协议(BaseProtocol的具象实现) ----------------------------

    // |--len--|--value--|
    // |--len--|--mtype--|--id_len--|--id--|--body--|
    class LVProtocol : public BaseProtocol
    {
    public:
        using ptr = std::shared_ptr<LVProtocol>;

        // 1. 判断当前缓冲区的数据是否足够处理
        virtual bool canProcessed(const BaseBuffer::ptr &buff) override
        {
            if (buff->readableSize() < lenFieldsLength)
            {
                return false;
            }
            int32_t total_len = buff->peekInt32();
            DLOG("total_len:%d", total_len);
            if (buff->readableSize() < (total_len + lenFieldsLength))
            {
                return false;
            }
            return true;
        }

        // 2. 对缓冲区数据进行处理，将其写入msg中
        virtual bool onMessage(const BaseBuffer::ptr &buff, BaseMessage::ptr &msg) override
        {
            // 当调用onMessage的时候, 默认认为缓冲区中的数据足够一条完整的消息
            int32_t total_len = buff->readInt32();  // 总长度
            MType mtype = (MType)buff->readInt32(); // 数据类型
            int32_t idlen = buff->readInt32();      // id长度

            // 剩余的数据就是body
            int32_t body_len = total_len - idlen - mtypeFieldsLength - idlenFieldsLength;

            string id = buff->retrieveAsString(idlen); // 根据id长度,读取id
            string body = buff->retrieveAsString(body_len);

            msg = MessageFactory::create(mtype);

            if (msg.get() == nullptr)
            {
                ELOG("消息类型错误,构造消息对象失败!");
                return false;
            }

            bool ret = msg->deserialize(body);
            if (ret == false)
            {
                ELOG("消息正文反序列化失败!");
                return false;
            }
            msg->setId(id);
            msg->setMType(mtype);
            return true;
        }

        // 3. 将消息msg进行序列化, 返回字符串 -- 发送
        virtual string serialize(const BaseMessage::ptr &msg) override
        {
            // |--len--|--mtype--|--id_len--|--id--|--body--|
            string body = msg->serialize();
            string id = msg->rid();
            auto mtype = htonl((int32_t)msg->mtype());
            int32_t idlen = htonl(id.size());
            int32_t h_total_len = mtypeFieldsLength + idlenFieldsLength + id.size() + body.size();
            int32_t n_total_len = htonl(h_total_len);
            // DLOG("h_total_len:%d", h_total_len);

            string result;
            result.reserve(h_total_len);
            result.append((char *)&n_total_len, lenFieldsLength);
            result.append((char *)&mtype, mtypeFieldsLength);
            result.append((char *)&idlen, idlenFieldsLength);

            result.append(id);
            result.append(body);

            return result;
        }

    private:
        const size_t lenFieldsLength = 4;
        const size_t mtypeFieldsLength = 4;
        const size_t idlenFieldsLength = 4;
    };

    // 生产LVProtocol的工厂类
    class ProtocolFactory
    {
    public:
        template <typename... Args>
        static BaseProtocol::ptr create(Args &&...args)
        {
            return std::make_shared<LVProtocol>(std::forward<Args>(args)...);
        }
    };

    //------------------------- 基于Muduo库 对BaseConnection进行具象实现 -----------------------------
    class MuduoConnection : public BaseConnection
    {
    public:
        using ptr = std::shared_ptr<MuduoConnection>;
        MuduoConnection(const muduo::net::TcpConnectionPtr &conn, const BaseProtocol::ptr &protocol) : _protocol(protocol), _conn(conn) {}

        // 发送消息 [需要Protocol类中的序列化函数] (_protocol.serialize())
        virtual void send(const BaseMessage::ptr &msg) override
        {
            // 将要发送的数据进行序列化
            string body = _protocol->serialize(msg);
            _conn->send(body); // 使用muduo库中的发送方法
        }

        // 关闭连接
        virtual void shutdown() override
        {
            _conn->shutdown();
        }
        // 连接状态
        virtual bool connected() override
        {
            return _conn->connected();
        }

    private:
        // 根据定制好的上层协议对数据进行序列化操作
        BaseProtocol::ptr _protocol;

        // 大部分的操作(关闭连接,查看连接状态) 是基于muduo库实现的
        muduo::net::TcpConnectionPtr _conn;
    };

    // 生产MuduoConnection的工厂类
    class ConnectionFactory
    {
    public:
        template <typename... Args>
        static BaseConnection::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoConnection>(std::forward<Args>(args)...);
        }
    };

    //-------------------------- MuduoServer 服务端的具象实现 [BaseServer] -------------------------------
    class MuduoServer : public BaseServer
    {
    public:
        using ptr = std::shared_ptr<MuduoServer>;

        MuduoServer(int port) : _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port),
                                        "MuduoServer", muduo::net::TcpServer::kReusePort),
                                _protocol(ProtocolFactory::create()) {}
        // 服务端启动
        virtual void start() override
        {
            // 1. 设置处理的回调函数
            _server.setConnectionCallback(bind(&MuduoServer::onConnection, this, std::placeholders::_1));
            _server.setMessageCallback(bind(&MuduoServer::onMessage,
                                            this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            // 2. 启动服务器

            // 开始监听
            _server.start();

            // 开启事件循环监控
            _baseloop.loop();
        }

    private:
        //  回调函数，用于处理新连接和IO信息
        void onConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            // 对连接进行封装并且管理
            if (conn->connected())
            {
                cout << "建立连接" << endl;
                auto muduo_conn = ConnectionFactory::create(conn, _protocol);

                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _conns.insert(std::make_pair(conn, muduo_conn));
                }

                if (_cb_connection)
                    _cb_connection(muduo_conn);
            }
            else
            {
                cout << "断开连接" << endl;
                BaseConnection::ptr muduo_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(conn);
                    // 连接确实不存在,关闭了
                    if (it == _conns.end())
                        return;

                    // 意外没有关闭
                    muduo_conn = it->second;
                    _conns.erase(conn);
                }
                // 如果设置了关闭的回调函数,则对其进行关闭
                if (_cb_close)
                    _cb_close(muduo_conn);
            }
        }

        // 回调函数，用于处理消息
        void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp)
        {
            DLOG("连接有数据到来,开始处理!");
            // 业务处理代码
            // 1. 将muduo库中的连接转换为BaseConnection连接
            auto base_buf = BufferFactory::create(buf); // 根据muduo库中的buf构造出BaseBuffer对象

            // 2. 对消息进行处理
            while (true)
            {
                if (_protocol->canProcessed(base_buf) == false)
                {
                    // 缓冲区中数据不足
                    if (base_buf->readableSize() > maxDataSize)
                    {
                        // 数据长度不足,但是又有非常多这样的数据[恶意垃圾数据]
                        conn->shutdown();
                        ELOG("缓冲区中数据过大!");
                        return;
                    }
                    // DLOG("数据量不足!");
                    break;
                }
                // DLOG("缓冲区中数据可处理!");
                BaseMessage::ptr msg;
                bool ret = _protocol->onMessage(base_buf, msg);
                if (ret == false)
                {
                    conn->shutdown();
                    ELOG("缓冲区中数据错误!");
                    return;
                }
                DLOG("消息反序列化成功!");
                BaseConnection::ptr base_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                    {
                        // 连接找不到,有问题;关闭连接
                        conn->shutdown();
                        return;
                    }
                    base_conn = it->second;
                }
                // DLOG("调用回调函数进行消息处理!");
                if (_cb_message)
                    _cb_message(base_conn, msg);
            }
        }

    private:
        const size_t maxDataSize = (1 << 16);
        // 创建连接需要Protocol [在Connection中的send方法需要对数据进行序列化,因此需要Protocol]
        BaseProtocol::ptr _protocol;
        muduo::net::EventLoop _baseloop; // 事件循环监控
        muduo::net::TcpServer _server;   // 通信连接管理

        // 对Tcp连接进行封装并且管理
        std::mutex _mutex;
        std::unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::ptr> _conns;
    };

    // 生产MuduoServer的工厂类
    class ServerFactory
    {
    public:
        template <typename... Args>
        static BaseServer::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoServer>(std::forward<Args>(args)...);
        }
    };

    // --------------------------------- MuduoClient ----------------------------------
    class MuduoClient : public BaseClient
    {
    public:
        using ptr = std::shared_ptr<MuduoClient>;

        MuduoClient(const string &sip, uint16_t sport) : _protocol(ProtocolFactory::create()),
                                                         _baseloop(_loopthread.startLoop()),
                                                         _downlatch(1), _client(_baseloop, muduo::net::InetAddress(sip, sport), "MuduoClient") {}
        // 客户端对服务端发起连接
        virtual void connect() override
        {
            DLOG("设置回调函数,连接服务器");
            _client.setConnectionCallback(bind(&MuduoClient::onConnection, this, std::placeholders::_1));
            _client.setMessageCallback(bind(&MuduoClient::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            // 连接服务器
            _client.connect();
            _downlatch.wait();
            DLOG("连接服务器成功");
        }

        // 关闭连接
        virtual void shutdown() override
        {
            return _client.disconnect();
        }

        // 发送消息
        virtual bool send(const BaseMessage::ptr &msg) override
        {
            if (connected() == false)
            {
                ELOG("连接已断开!");
                return false;
            }
            _conn->send(msg);
            return true;
        }

        // 获取连接对象
        virtual BaseConnection::ptr connection() override
        {
            return _conn;
        }

        // 判断连接是否正常
        virtual bool connected() override
        {
            return (_conn && _conn->connected());
        }

    private:
        //  回调函数，用于处理新连接和IO信息
        void onConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                cout << "建立连接" << endl;
                _downlatch.countDown(); // 计数--，为0阻塞 [唤醒构造函数中的阻塞函数]
                _conn = ConnectionFactory::create(conn, _protocol);
            }
            else
            {
                cout << "断开连接" << endl;
                _conn.reset();
            }
        }

        // 回调函数，用于处理消息
        void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp)
        {
            DLOG("连接有数据到来,开始处理!");
            // 业务处理代码
            // 1. 将muduo库中的连接转换为BaseConnection连接
            auto base_buf = BufferFactory::create(buf); // 根据muduo库中的buf构造出BaseBuffer对象

            // 2. 对消息进行处理
            while (true)
            {
                if (_protocol->canProcessed(base_buf) == false)
                {
                    // 缓冲区中数据不足
                    if (base_buf->readableSize() > maxDataSize)
                    {
                        // 数据长度不足,但是又有非常多这样的数据[恶意垃圾数据]
                        conn->shutdown();
                        ELOG("缓冲区中数据过大!");
                        return;
                    }
                    DLOG("数据量不足!");
                    break;
                }
                DLOG("缓冲区中数据可处理!");
                BaseMessage::ptr msg;
                bool ret = _protocol->onMessage(base_buf, msg);
                if (ret == false)
                {
                    conn->shutdown();
                    ELOG("缓冲区中数据错误!");
                    return;
                }
                DLOG("缓冲区中数据解析完毕,调用回调函数进行处理!");
                if (_cb_message)
                    _cb_message(_conn, msg);
            }
        }

    private:
        const size_t maxDataSize = (1 << 16);
        BaseProtocol::ptr _protocol;

        BaseConnection::ptr _conn;
        muduo::CountDownLatch _downlatch;
        muduo::net::EventLoopThread _loopthread;
        muduo::net::EventLoop *_baseloop;

        //_client类是发送不了数据的
        muduo::net::TcpClient _client;
    };

    // 生产MuduoClient的工厂类
    class ClientFactory
    {
    public:
        template <typename... Args>
        static BaseClient::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoClient>(std::forward<Args>(args)...);
        }
    };
}